/**
 * 金币交易系统串联测试脚本
 * 覆盖：1455(聚合) → 1463(买/卖各一次) → 1456(我的交易)
 * 支持探活与环境变量：SVC_HOST/SVC_PORT/SVC_PROTO/TEST_MOBILE/TEST_PWD_HASH
 */

const http = require('http');
const WebSocket = require('ws');

const CONFIG = {
    server: {
        host: process.env.SVC_HOST || 'localhost',
        port: Number(process.env.SVC_PORT || 3000),
        protocol: process.env.SVC_PROTO || 'http',
    },
    testUser: {
        mobile: process.env.TEST_MOBILE || '17091279999',
        passwordHash:
            process.env.TEST_PWD_HASH || 'e10adc3949ba59abbe56e057f20f883e',
    },
};

class GoldTradeTester {
    constructor() {
        this.ws = null;
        this.token = null;
        this.userId = null;
        this.roles = [];
        this.roleId = null;
    }

    async run() {
        console.log('🏯 金币交易系统串联测试开始...');
        await this.waitServer();
        await this.ensureUser();
        await this.login();
        await this.openWS();
        await this.queryRoles();
        while (this.roles.length < 2) {
            await this.createRole(`金币交易测试${this.roles.length + 1}`);
            await this.delay(600);
            await this.queryRoles();
        }
        // A号用于卖，B号用于买
        const roleA = this.roles[0].id;
        const roleB = this.roles[1].id;
        await this.selectRole(roleA);

        // 1) 聚合
        const agg1 = await this.sendAndWait(1455, { type: 1 });
        if (agg1.code !== 0) throw new Error('1455 查询失败');
        console.log('📊 初始聚合(1455):', JSON.stringify(agg1));

        // 2) 卖盘侧撮合：A挂卖，B买
        const priceSell = 1000;
        await this.assertPlace(2, 1, priceSell); // A卖1枚@1000
        const preBuyer = await this.queryBalance(roleB);
        await this.selectRole(roleB);
        await this.assertPlace(1, 1, priceSell); // B买1枚@1000
        await this.delay(300);
        const postBuyer = await this.queryBalance(roleB);
        if (!(postBuyer.gold >= preBuyer.gold + 1)) {
            console.warn('⚠️ 买方金币未如预期增加，实际：', preBuyer.gold, '→', postBuyer.gold);
        }

        // 3) 买盘侧撮合：B挂买，A卖（校验A净入账≥应得-3%）
        const priceBuy = 800;
        await this.assertPlace(1, 1, priceBuy); // B买1枚@800
        const preSeller = await this.queryBalance(roleA);
        await this.selectRole(roleA);
        await this.assertPlace(2, 1, priceBuy); // A卖1枚@800 (应成交)
        await this.delay(300);
        const postSeller = await this.queryBalance(roleA);
        const gross = priceBuy * 1;
        const fee = Math.floor(gross * 0.03);
        const net = gross - fee;
        const delta = postSeller.money - preSeller.money;
        if (delta < net) {
            console.warn(`⚠️ 卖方净入账校验：应≥${net}，实得 ${delta}`);
        } else {
            console.log(`✅ 卖方净入账 OK: +${delta} (预期≥${net})`);
        }

        // 4) 我的交易（1456）
        const my = await this.sendAndWait(1456, { pageNum: 0 });
        if (my.code !== 0) throw new Error('1456 查询失败');
        console.log('📜 我的交易(1456) 条目数：', (my.list || []).length);

        // 5) 再次聚合（1455）
        const agg2 = await this.sendAndWait(1455, { type: 1 });
        console.log('📊 成交后聚合(1455):', JSON.stringify(agg2));

        await this.closeWS();
        console.log('✅ 金币交易系统串联测试完成');
    }

    // ============ 基础能力 ============
    async waitServer(maxRetry = 20) {
        for (let i = 0; i < maxRetry; i++) {
            try {
                await this.httpReq('/user-service/user/login', 'POST', {
                    mobile: 'x',
                    pwd: 'x',
                });
                return;
            } catch (e) {
                if (String(e?.message || '').includes('ECONNREFUSED')) {
                    console.log(`⏳ 等待服务启动(${i + 1}/${maxRetry})... http://${CONFIG.server.host}:${CONFIG.server.port}`);
                    await this.delay(1500);
                    continue;
                }
                return;
            }
        }
        throw new Error(`无法连接到服务 ${CONFIG.server.host}:${CONFIG.server.port}`);
    }

    async ensureUser() {
        try {
            await this.httpReq('/user-service/user/register', 'POST', {
                mobile: CONFIG.testUser.mobile,
                pwd: CONFIG.testUser.passwordHash,
                captcha: '123456',
            });
            console.log('📝 注册成功');
        } catch (e) {
            console.log('👤 注册跳过(可能已存在):', e.message);
        }
    }

    async login() {
        const ret = await this.httpReq('/user-service/user/login', 'POST', {
            mobile: CONFIG.testUser.mobile,
            pwd: CONFIG.testUser.passwordHash,
        });
        if (ret.code !== 0) throw new Error(ret.msg || '登录失败');
        this.userId = ret.data.userId;
        this.token = ret.data.accessToken;
    }

    async openWS() {
        const url = `ws://${CONFIG.server.host}:${CONFIG.server.port}/user-service/websocket?t=${this.token}`;
        this.ws = new WebSocket(url);
        await new Promise((resolve, reject) => {
            const t = setTimeout(() => reject(new Error('WS连接超时')), 15000);
            this.ws.on('open', () => {
                clearTimeout(t);
                resolve();
            });
            this.ws.on('error', (e) => {
                clearTimeout(t);
                reject(e);
            });
        });
    }

    async closeWS() {
        try { this.ws?.close?.(); } catch { }
    }

    async queryRoles() {
        const res = await this.send(3, {});
        this.roles = (res && res.roleList) || [];
    }

    async createRole(name) {
        await this.send(5, { name, sex: 1, roleType: 1, headIndex: 2 });
    }

    async selectRole(id) {
        const res = await this.sendAndWait(4, { characterId: id });
        if (res.code !== 0) throw new Error('角色选择失败');
        this.roleId = id;
    }

    async queryBalance(roleId) {
        // 使用 1451：查询金币/银两
        const ret = await this.sendAndWait(1451, {});
        if (ret.code !== 0) return { gold: 0, money: 0 };
        const d = ret.data || {};
        return { gold: Number(d.gold || 0), money: Number(d.money || 0) };
    }

    async assertPlace(type, count, price) {
        const res = await this.sendAndWait(1463, { type, count, price });
        if (res.code !== 0) throw new Error(`1463 下单失败: ${res.msg || ''}`);
    }

    // ============ WS 辅助 ============
    async send(actId, payload = {}) {
        return new Promise((resolve) => {
            const handler = (raw) => {
                try {
                    const msg = JSON.parse(raw.toString());
                    if (msg && msg.act_id === actId) {
                        this.ws.off('message', handler);
                        resolve(msg);
                    }
                } catch { }
            };
            this.ws.on('message', handler);
            this.ws.send(JSON.stringify({ c: actId, d: Object.keys(payload).length ? payload : null }));
            setTimeout(() => {
                this.ws.off('message', handler);
                resolve({ act_id: actId, code: -1, msg: 'timeout' });
            }, 15000);
        });
    }

    async sendAndWait(actId, payload = {}) {
        const res = await this.send(actId, payload);
        return res;
    }

    async httpReq(path, method, body) {
        const options = {
            hostname: CONFIG.server.host,
            port: CONFIG.server.port,
            path,
            method,
            headers: { 'Content-Type': 'application/json' },
        };
        return new Promise((resolve, reject) => {
            const req = http.request(options, (res) => {
                let data = '';
                res.on('data', (c) => (data += c));
                res.on('end', () => {
                    try {
                        resolve(JSON.parse(data || '{}'));
                    } catch {
                        reject(new Error('响应解析失败'));
                    }
                });
            });
            req.on('error', reject);
            if (body) req.write(JSON.stringify(body));
            req.end();
        });
    }

    delay(ms) { return new Promise((r) => setTimeout(r, ms)); }
}

(async () => {
    try {
        const t = new GoldTradeTester();
        await t.run();
    } catch (e) {
        console.error('❌ 测试失败:', e?.message || e);
        process.exit(1);
    }
})();


